/**
 * WTF Engine
 *
 * License... etc.
 **
 * String wrapper class & String Pool
 **
 * Author: Sean Micklethwaite
 **/

#ifndef __stringH__
#define __stringH__

#include "config.h"
#include <string>

#ifdef __UNICODE__
#	include <unicode/ustring.h>
#	include <unicode/unistr.h>
	typedef UnicodeString	tBaseString;
	typedef UChar			tChar;
#else
	typedef std::string		tBaseString;
	typedef char			tChar;
#endif


#include <set>
#include "wtftypes.h"
#include "mm.h"


namespace WtfEngine
{
	class String: public tBaseString {
#ifdef __UNICODE__
		mutable char * msCString;

	public:
		// Constructors / Implicit casts
		String(): tBaseString(), msCString(NULL) {};
		String(const char * s): tBaseString(s), msCString(NULL) {};
		String(const String& s): tBaseString(*(tBaseString*)&s), msCString(NULL) {};
		String(const tBaseString& s): tBaseString(s), msCString(NULL) {};
		String(const std::string& s): tBaseString(s.c_str()), msCString(NULL) {};

		~String();

		/// Returns NULL terminated array of UTF8 characters
		const char * c_str() const;
#else
	public:
		// Constructors / Implicit casts
		String(): tBaseString() {};
		String(const char * s): tBaseString(s) {};
		String(const String& s): tBaseString(*(tBaseString*)&s) {};
		String(const tBaseString& s): tBaseString(s) {};
#endif

		/// Returns a NULL terminated array of characters of the base class's native type
		const tChar * str() const {
#ifdef __UNICODE__
			const tChar * sBuf = getBuffer();
			
			//
			// Buffer may not be NULL terminated, ensure that it is before
			// we return the string.
			//

			if((length() >= getCapacity()) || (sBuf[length()] != '\0')) {
				// Eww... I taste vomit in my mouth.
				int len = length();
				sBuf = const_cast<String *>(this)->getBuffer(length() + 1);
				const_cast<tChar *>(sBuf)[len] = '\0';
				const_cast<String *>(this)->releaseBuffer(len);

				sBuf = getBuffer();
			};

			assert(sBuf[length()] == '\0');
			return sBuf;
#else
			return c_str();
#endif
		};


#ifdef __UNICODE__
		
		/* Maintain compatibility with std::string */

		/// substr (offset: Int, length: Int): String
		/// @post	r = s[offset, offset + length)
		inline String substr(int offset, int length) {
			String s;
			extractBetween(offset, offset + length, s);
			return s;
		};

#endif

		/**** Delegates ****/

		inline void operator = (const char * s) {
			tBaseString::operator =(s);
		};
		inline void operator = (const String& s) {
			tBaseString::operator =(s);
		};

		inline String operator + (const String& s) const {
			String sNew(*this);
			sNew += s;
			return sNew;
		};

		inline bool operator < (const String& s) const {
			return (tBaseString::operator <(s) == TRUE);
		};

		
#ifdef __UNICODE__

		inline int IndexOf(tChar c, int offset = 0) const {
			return indexOf(c, offset);
		};

		inline int IndexOf(char c, int offset = 0) const {
			tChar uc;
			u_charsToUChars(&c, &uc, 1);
			return IndexOf(uc, offset);
		};

		class ConvertedStringWrapper {
			char * msString;

		public:
			ConvertedStringWrapper(const String& s) {
				// Ugly conversion, hopefully this won't happen too often.
				msString = new char[s.length() + 1];
				u_strToUTF8(msString, s.length() + 1, NULL, s.getBuffer(), s.length(), NULL);
			};
			~ConvertedStringWrapper() {
				delete msString;
			};

			/// Cast to std::string
			operator std::string () const {
				return std::string(msString);
			};
			/// Cast to const char *
			operator const char * () const {
				return msString;
			};
		};

		/// Cast to std::string
		operator std::string () const;

#else

		inline int IndexOf(tChar c, int offset = 0) const {
			return find_first_of(c, offset);
		};

#endif
		/// Cast to base string type
		operator tBaseString () {
			return *(tBaseString*) this;
		};
		operator const tBaseString () const {
			return *(const tBaseString*) this;
		};

		
		// Serialization
		virtual unsigned CalculateSerializedSize() const {
			return (sizeof(tChar) * (length() + 1));
		};
		virtual unsigned Serialize(void * pBuf) const {

			//
			// Format is:
			//	Data string (null terminated, variable size)
			//	Data type id (int)
			//

			tChar * pString = reinterpret_cast<tChar *>(pBuf);
			const tChar * pSrc = str();
			
			do {
				*pString++ = *pSrc;	
			} while(*pSrc++);
			
			return (unsigned)pString - (unsigned)pBuf;
		};
		virtual unsigned Deserialize(void * pBuf) {
			tChar * pString = reinterpret_cast<tChar *>(pBuf);

			// XXX - this can crash if the string is not NULL terminated. Better
			// be well validated!
			for(*this = ""; *pString; pString++) {
				*this += *pString;
			};

			return (sizeof(tChar) * (length() + 1));
		};
	};
	
	/**
	 * A place to store strings.
	 **/
	class StringPool : public Singleton<StringPool>
	{
		typedef String	tRealEntry;

		std::set<tRealEntry>	mvsStringPool;
		
		void RemoveEntry(tRealEntry *pString) {
			mvsStringPool.erase(*pString);
		};

	public:
		class Entry {
			const tRealEntry * mpString;

		public:
			Entry(): mpString(NULL) {
			};

			explicit Entry(const tRealEntry* s): mpString(s) {
			};

			/// Implicit cast from string
			Entry(const String& s) {
				*this = StringPool::GetInstance()[s];
			};
			/// Implicit cast from const char *
			Entry(const char * s) {
				*this = StringPool::GetInstance()[String(s)];
			};

			~Entry() {
			};

			inline bool operator < (const Entry& s) const {
				// As string pool entries and their real strings are a one to one
				// map, we can safely compare the pointers to entries as a faster
				// substitute for comparing the strings.
				return (mpString < s.mpString);
			};
			inline bool operator == (const Entry& s) const {
				return (mpString == s.mpString);
			};

			// String cast operator - creates a copy of the string, safe for external use
			inline operator String () const {
				return String(*mpString);
			};
			inline const String& operator * () const {
				return *mpString;
			};

			inline bool isNull() const {
				return (mpString == NULL);
			};
		};

		// Adds / fetches a string entry from the string pool.
		Entry operator [](const String& s){
			return Entry(&*(mvsStringPool.insert(s).first));
		};
	};
};

#endif
